﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

using CTRL;
using BLL;
using DAL;

namespace GUI_Client
{
    public partial class GUINewBooking : Form
    {
        public static Customer Customer { get; private set; }
        public static Car Car { get; private set; }

        private static volatile GUINewBooking instance;

        private CtrlMap ctrlMap;
        private CtrlCar ctrlCar;
        private CtrlBooking ctrlBooking;
        private CtrlBatteryItem ctrlBatteryItem;
        private CtrlCustomer ctrlCustomer;

        private List<BatteryCenterDistance> distanceList;
        private Dictionary<BatteryCenter, BatteryItem> bookingList;

        public GUINewBooking()
        {
            InitializeComponent();

            ctrlMap = CtrlMap.Instance;
            ctrlCar = CtrlCar.Instance;
            ctrlBooking = CtrlBooking.Instance;
            ctrlCustomer = CtrlCustomer.Instance;
            ctrlBatteryItem = CtrlBatteryItem.Instance;

            bookingList = new Dictionary<BatteryCenter, BatteryItem>();

            //Customer = GUIMain.Customer;

            Customer = ctrlCustomer.GetCustomer(1);
            ddFrom.DisplayMember = "name";
            ddFrom.DataSource = new BindingSource(ctrlMap.GetAllCities(), "");

            List<Car> listCar = ctrlCar.GetAllCarsOwnedByCustomer(Customer.customerID).ToList();

            if (listCar.Any())
            {
                ddCar.DisplayMember = "licencePlate";
                ddCar.ValueMember = "licencePlate";
                ddCar.DataSource = listCar;
            }
        }

        private void ddFrom_SelectedIndexChanged(object sender, EventArgs e)
        {
            List<BatteryCenter> listTo = ctrlMap.GetAllCities().ToList();

            listTo.Remove(listTo.Find(x => x.name.Equals(ddFrom.Text)));

            ddTo.DisplayMember = "name";
            ddTo.DataSource = new BindingSource(listTo, "");
        }

        private void btnCalcRoute_Click(object sender, EventArgs e)
        {

            BatteryCenter from = ctrlMap.FindCity(ddFrom.Text);
            BatteryCenter to = ctrlMap.FindCity(ddTo.Text);

            gvCityList.Rows.Clear();

            distanceList = ctrlMap.GetPathFromCityToCity(from, to, Car.range);

            if (bookingList.Count > 0)
                ctrlBooking.UnReserveBatteries(bookingList.Select(x => x.Value).ToList());

            double totalDistance = 0.0;
            double batteryDistance = 0.0;

            int count = 1;
            int batteryAvailable = 0;

            string impossibleRoute = "";

            bool errorMsg = false;

            foreach (var i in distanceList)
            {
                string batteryNumber = i.Stop ? ctrlBatteryItem.FindBatteryItemsAtLocationState(i.BatteryCenter.name, Car.capacity, BatteryState.available).ToList().Count.ToString() : "not needed";

                batteryDistance = i.Stop ? 0 : batteryDistance + i.Distance;

                gvCityList.Rows.Add
                (
                    count++,
                    i.BatteryCenter.name,
                    String.Format("{0:n} km", i.Distance),
                    String.Format("{0:n} km", (totalDistance += i.Distance)),
                    batteryDistance,
                    batteryNumber
                );

                if (i.Stop)
                {
                    ColorCellAccordingToBatteryNumber(gvCityList, Int32.Parse(batteryNumber));
                    ColorGridViewLastRow(gvCityList, Color.LightGreen);

                    if (Int32.Parse(batteryNumber) > 0)
                        batteryAvailable++;
                }
            }

            int stops = distanceList.FindAll(x => x.Stop).ToList().Count;

            if (batteryAvailable != stops)
            {
                int failBatteryCenters = stops - batteryAvailable;

                impossibleRoute += "On " + failBatteryCenters + " battery center" + (failBatteryCenters == 1 ? "" : "s") + " there is no available battery for replacement.";
                errorMsg = true;
            }

            if (stops == 0)
            {
                impossibleRoute += "Your route has no stops, therefore nothing to book.";
                errorMsg = true;
            }

            if (distanceList.Count < 2)
            {
                impossibleRoute += "Your car does not have enough range to go to the destination.";
                errorMsg = true;
                gvCityList.Rows.Clear();
            }

            if (errorMsg)
            {
                MessageBox.Show(impossibleRoute, "Impossible route", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                lblInfoUpdate(stops, totalDistance);
                bookingList = ctrlBooking.ReserveBatteries(distanceList, Car);
            }

            btnUpdate(errorMsg);
        }

        private void btnUpdate(bool update)
        {
            btnBook.Enabled = !update;
            btnShowMap.Enabled = !update;

            if (update)
                lblUpdate();
        }

        private void lblUpdate()
        {
            lblTripTimeInfo.Text = "";
            lblStopsInfo.Text = "";
            lblTotalKMInfo.Text = "";
        }

        private void lblInfoUpdate(int stops, double totalDistance)
        {
            double totalDistanceWithStops = totalDistance + (stops * 10);

            TimeSpan ts = TimeSpan.FromHours(totalDistanceWithStops / 120);

            lblTripTimeInfo.Text = ts.Hours + " hour" + (ts.Hours == 1 ? "" : "s") + " and " + ts.Minutes + " minute" + (ts.Minutes == 1 ? "" : "s");
            lblStopsInfo.Text = stops.ToString();
            lblTotalKMInfo.Text = totalDistance.ToString();
        }

        private void gvCityList_SelectionChanged(Object sender, EventArgs e)
        {
            gvCityList.ClearSelection();
        }

        private void ddCar_SelectedIndexChanged(object sender, EventArgs e)
        {
            Car = ctrlCar.FindCarByLicencePlate(ddCar.SelectedValue as string);

            lblCarRangeInfo.Text = Car.range + " km";
        }

        private void btnBook_Click(object sender, EventArgs e)
        {
            if (ctrlBooking.CreateBooking(Car.carID, distanceList) > 0)
            {
                MessageBox.Show("Your booking was successfully recorded.\nReturning you back to main menu.");
                bookingList.Clear();
                back_Click(null, null);
            }
            else
                MessageBox.Show("There was some kind of an error while booking your route.");
        }

        private void btnShowMap_Click(object sender, EventArgs e)
        {
            ShowMap showMap = new ShowMap(distanceList);
            showMap.Show();
        }

        private void back_Click(object sender, EventArgs e)
        {
            GUIMain main = GUIMain.Instance;
            main.Show();

            this.Hide();
            gvCityList.Rows.Clear();
            btnUpdate(true);
            lblUpdate();

            if (bookingList.Count > 0)
                ctrlBooking.UnReserveBatteries(bookingList.Select(x => x.Value).ToList());
        }

        private void ColorGridViewLastRow(DataGridView gv, Color color)
        {
            gv.Rows[gv.RowCount - 1].DefaultCellStyle.BackColor = color;
        }

        private void ColorCellAccordingToBatteryNumber(DataGridView gv, int batteryNumber)
        {
            gv.Rows[gv.RowCount - 1].Cells[5].Style.BackColor = (batteryNumber == 0 ? Color.DarkRed : (batteryNumber < 2 ? Color.OrangeRed : (batteryNumber < 5 ? Color.Orange : (batteryNumber < 10 ? Color.LightYellow : Color.LightGreen))));
        }

        private void GUINewBooking_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (bookingList.Count > 0)
                ctrlBooking.UnReserveBatteries(bookingList.Select(x => x.Value).ToList());
        }

        public static GUINewBooking Instance
        {
            get
            {
                if (instance == null)
                    instance = new GUINewBooking();

                return instance;
            }
        }
    }
}